#include <retro.h>
#include "retro.creator.h"
#include "retro.scriptingapi.h"
#include <library/retro.api.h>

using namespace Retro::Creator;
using namespace Retro::Creator::Editor;
using namespace Retro::Scripting;

enum ChildID
{
  ChildEdit = 0x1,
  ChildToolbar,
};

enum ScriptItem
{
  ScriptItemCheckSyntax    = EditorItemUser,
};

ScriptEditor::ScriptEditor() :
  EditorBase(Data::ResourceScript),
  mManager(NULL)
{
}

VOID    ScriptEditor::onCreate(Event::WndCreateEvent& iEvent)
{
  mEdit.create(*this, _T("Scintilla"), _T(""),
    WS_CHILD | ES_LEFT | ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN);
  mEdit.setID(ChildEdit);

  API::InitializeEditor(mEdit, Data::ScriptLua);

  mLayout = getHandle();
  mLayout.addFill(mEdit);

  addStyle(WS_TABSTOP);
}

VOID    ScriptEditor::onDestroy()
{
  mEdit.destroy();
}

VOID    ScriptEditor::onToolbar(ToolBarNotify* iData)
{
  switch (iData->itemID)
  {
  case ScriptItemCheckSyntax:
    validateSyntax();
    break;
  }
}

VOID    ScriptEditor::onNotify(Event::NotifyEvent& iEvent)
{
  SCNotification* notify = (SCNotification*)iEvent.data;
  LRESULT line;

  if (!getScript() || !isVisible())
  {
    EditorBase::onNotify(iEvent);
    return;
  }

  switch (iEvent.data->idFrom)
  {
  case ChildEdit:
    switch (iEvent.data->code)
    {
    case SCN_MARGINCLICK:
      line = mEdit.send(SCI_LINEFROMPOSITION, notify->position, 0);

      switch (notify->margin)
      {
      case SCRIPT_MARGIN_FOLD:
        mEdit.send(SCI_TOGGLEFOLD, line, 0);
        break;
      }
      break;
    case SCN_ZOOM:
      EditorBase::setZoom(getZoom());
      break;
    case SCN_MODIFIED:
      if (notify->modificationType & SC_MOD_INSERTTEXT ||
          notify->modificationType & SC_MOD_DELETETEXT)
      {
        getScript()->setUnsaved();
        sendNotify(EditorNotifyEdit);
      }

      if (notify->modificationType & SC_PERFORMED_UNDO)
      {
        getScript()->setUnsaved();
        sendNotify(EditorNotifyUndo);
      }

      if (notify->modificationType & SC_PERFORMED_REDO)
      {
        getScript()->setUnsaved();
        sendNotify(EditorNotifyRedo);
      }
      break;
    }
    break;
  }

  EditorBase::onNotify(iEvent);
}

VOID    ScriptEditor::onSize(Event::SizeEvent& iEvent)
{
  mLayout.doLayout();
}

VOID    ScriptEditor::onFocus(Event::FocusEvent& iEvent)
{
  if (iEvent.focused)
    mEdit.focus();
}

VOID    ScriptEditor::setResource(Data::Resource* iValue)
{
  if (getResource() != iValue)
  {
    if (getResource())
      flush(getResource());

    EditorBase::setResource(iValue);

    if (iValue)
    {
      mManager = new Scripting::ScriptManager();
      Scripting::InitializeAPI(mManager);

      mEdit.send(SCI_SETTEXT, NULL, (LPARAM)getScript()->getBuffer());
      mEdit.send(SCI_EMPTYUNDOBUFFER, NULL, NULL);

      setZoom(100);
      show();
    }
    else
    {
      SafeDelete(mManager);
      mEdit.send(SCI_SETTEXT, NULL, (LPARAM)"");

      hide();
    }
  }
}

VOID    ScriptEditor::initToolbar()
{
  ToolItem items[] = {
    {0,                         _T("Syntax"),       ToolItemLabel},
    {ScriptItemCheckSyntax,     _T("Check"),        ToolItemButton},
    {0,                         _T("Zoom"),         ToolItemLabel},
    {EditorItemZoom,            _T(""),             ToolItemMenu,
    getMenuZoom()},
  };

  getToolBar()->addItems(items, sizeof(items) / sizeof(ToolItem));

  EditorBase::initToolbar();
}

VOID    ScriptEditor::clearUndo()
{
  mEdit.send(SCI_EMPTYUNDOBUFFER, NULL, NULL);
}

INT     ScriptEditor::getZoom()
{
  return INT(mEdit.send(SCI_GETZOOM, NULL, NULL) + 20) * 100 / 20;
}

VOID    ScriptEditor::setZoom(INT value)
{
  mEdit.send(SCI_SETZOOM, (WPARAM)(value * 20 / 100 - 20), NULL);
  EditorBase::setZoom(getZoom());
}

INT     ScriptEditor::getLineSize()
{
  return 1;
}

BOOL    ScriptEditor::canUndo()
{
  return (BOOL)mEdit.send(SCI_CANUNDO, NULL, NULL);
}

BOOL    ScriptEditor::canRedo()
{
  return (BOOL)mEdit.send(SCI_CANREDO, NULL, NULL);
}

BOOL    ScriptEditor::undo()
{
  return (BOOL)mEdit.send(SCI_UNDO, NULL, NULL);
}

BOOL    ScriptEditor::redo()
{
  return (BOOL)mEdit.send(SCI_REDO, NULL, NULL);
}

BOOL    ScriptEditor::save(HANDLE iFile)
{
  Data::Script  wScript;

  memcpy(&wScript, getScript(), sizeof(wScript));
  wScript.buffer = NULL;

  flush(&wScript);

  BOOL wResult = wScript.save(iFile);

  wScript.free();
  return wResult;
}

VOID    ScriptEditor::flush(Data::Resource* oResource)
{
  Data::Script* wScript = (Data::Script*)oResource;

  LRESULT wTextLength = mEdit.send(SCI_GETTEXTLENGTH, 0, 0) + 1;
  PSTR    wText       = (char*)malloc(wTextLength);

  mEdit.send(SCI_GETTEXT, wTextLength, (LPARAM)wText);
  wScript->setBuffer(wText);

  free(wText);
}

BOOL    ScriptEditor::validateSyntax()
{
  LRESULT wTextlength = mEdit.send(SCI_GETTEXTLENGTH, 0, 0) + 1;
  PSTR wText          = (char*)malloc(wTextlength);

  mEdit.send(SCI_GETTEXT, wTextlength, (LPARAM)wText);

  Scripting::ScriptInterface* wScript =
    mManager->getInterface(getScript()->getLanguage());
  BOOL wResult = wScript->validate(wText);

  Frame::get()->setStatus(FrameEditor, wResult ?
    _T("Script syntax valid") : wScript->getErrorMessage());

  free(wText);

  return wResult;
}
